﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CoreUtils.WIL
{
    /// <summary>
    /// Histogram transformation functions from WIL library and drawing
    /// </summary>
    public static class ColorMode
    {
        public const long RED = 2,
        GREEN = 4,
        BLUE = 8;
    }

    public class Histogram
    {
        public static long m_colorChecked;
        public static bool m_axesChecked;
        public static bool m_peaksChecked;
        public static bool m_valleysChecked;
        public static int m_interval;

        #region Logic functions
        public static bool hasCheckedColor(long colorMode)
        {
            return (m_colorChecked & colorMode) == colorMode;
        }

        public static void addCheckedColor(long colorMode)
        {
            m_colorChecked |= colorMode;
        }

        public static void findPeaks(FVIL.Measure.CFviHistogramResult[] result, ref List<int>[] lsPeak,
            ref List<int>[] lsValley)
        {
            for (int i = 0; i < lsPeak.Length; i++)
            {
                lsPeak[i] = new List<int>();
                lsValley[i] = new List<int>();
                for (int j = m_interval; j < result[i].Count - m_interval; j++)
                {
                    List<double> lsFreq = new List<double>();
                    for (int k = j - m_interval; k < j + m_interval; k++)
                    {
                        lsFreq.Add(result[i][k]);
                    }
                    if (result[i][j] == lsFreq.Max())
                        lsPeak[i].Add(j);
                    if (result[i][j] == lsFreq.Min())
                        lsValley[i].Add(j);
                }
            }
        }
        #endregion

        #region Draw Histogram
        public static void drawRuler(ref FVIL.GDI.CFviOverlay pOverlay, FVIL.Data.CFviPoint stPoint,
            FVIL.Data.CFviPoint edPoint, bool isPercentageMode)
        {
            // Draw starting and ending point with arrow 
            FVIL.Data.CFviPoint[] lsPoint = new FVIL.Data.CFviPoint[2] { stPoint, edPoint };
            for (int i = 0; i < 2; i++)
            {
                FVIL.GDI.CFviGdiAnchor anchor = new FVIL.GDI.CFviGdiAnchor(lsPoint[i], lsPoint[1 - i]);
                anchor.Pen.Color = System.Drawing.Color.Chocolate;
                anchor.Pen.Width = 2;
                anchor.Style = FVIL.GDI.FigureStyle.Cross;
                anchor.Position = lsPoint[i];
                pOverlay.Figures.Add(anchor);
            }

            // Draw line connecting 2 points
            FVIL.Data.CFviLineSegment lineseg = new FVIL.Data.CFviLineSegment(lsPoint[0], lsPoint[1]);
            FVIL.GDI.CFviGdiLineSegment line = new FVIL.GDI.CFviGdiLineSegment(lineseg);
            line.Pen.Color = System.Drawing.Color.Chocolate;
            line.Pen.Width = 2;
            pOverlay.Figures.Add(line);

            // Draw mark points
            int nbMark = 4;
            List<FVIL.Data.CFviPoint> lsMarkPoint = new List<FVIL.Data.CFviPoint>();
            int horzStep = (int)(edPoint.X - stPoint.X) / nbMark;
            int vertStep = (int)(edPoint.Y - stPoint.Y) / nbMark;
            for (int i = 0; i < nbMark + 1; i++)
            {
                double posX = stPoint.X + i * horzStep;
                double posY = stPoint.Y + i * vertStep;
                FVIL.Data.CFviPoint newPoint = new FVIL.Data.CFviPoint(posX, posY);
                FVIL.GDI.CFviGdiAnchor anchor = new FVIL.GDI.CFviGdiAnchor(newPoint, edPoint);
                anchor.Pen.Color = System.Drawing.Color.Chocolate;
                anchor.Pen.Width = 2;
                anchor.Style = FVIL.GDI.FigureStyle.Cross;
                anchor.Position = newPoint;
                pOverlay.Figures.Add(anchor);

                FVIL.GDI.CFviGdiString mark = new FVIL.GDI.CFviGdiString();
                if (isPercentageMode)
                {
                    mark.Text = ((int)100 * i / nbMark).ToString();
                }
                else
                {
                    mark.Text = Math.Max(i * horzStep, i * vertStep).ToString();
                }
                mark.Font.Weight = FVIL.GDI.FontWeight.Bold;
                mark.Font.Width = 6;
                mark.Font.Height = 12;
                mark.Align = FVIL.GDI.TextAlign.Left | FVIL.GDI.TextAlign.Top;
                mark.Color = System.Drawing.Color.Chocolate;
                mark.Position = newPoint + new FVIL.Data.CFviPoint(3, 3);
                pOverlay.Figures.Add(mark);
            }
        }

        public static void drawRectangle(ref FVIL.GDI.CFviOverlay pOverlay, FVIL.Data.CFviRectangle rect)
        {
            // Draw rectangle
            FVIL.GDI.CFviGdiRectangle dRect = new FVIL.GDI.CFviGdiRectangle(rect);
            dRect.Pen.Color = System.Drawing.Color.FromArgb(0xFF, 0xFF, 0x00);
            dRect.Pen.Width = 1;
            pOverlay.Figures.Add(dRect);
        }

        public static void drawPeaks(ref FVIL.GDI.CFviOverlay pOverlay, FVIL.Measure.CFviHistogramResult result,
            List<int> lsPeak, List<int> lsValley, int vert)
        {
            if (m_peaksChecked)
            {
                for (int i = 0; i < lsPeak.Count; i++)
                {
                    double freq = result[lsPeak[i]];
                    double ratio = freq / result.MaxFrequency;
                    FVIL.Data.CFviPoint peakPoint = new FVIL.Data.CFviPoint(lsPeak[i], vert - (vert * ratio));
                    FVIL.GDI.CFviGdiPoint gPeakPoint = new FVIL.GDI.CFviGdiPoint(peakPoint);
                    gPeakPoint.Pen.Color = System.Drawing.Color.Cyan;
                    gPeakPoint.Pen.Width = 2;
                    gPeakPoint.Style = FVIL.GDI.FigureStyle.Xmark;
                    pOverlay.Figures.Add(gPeakPoint);

                    FVIL.GDI.CFviGdiString mark = new FVIL.GDI.CFviGdiString();
                    mark.Text = lsPeak[i].ToString();
                    mark.Font.Width = 6;
                    mark.Font.Height = 12;
                    mark.Align = FVIL.GDI.TextAlign.Left | FVIL.GDI.TextAlign.Top;
                    mark.Color = System.Drawing.Color.Cyan;
                    mark.Position = peakPoint;
                    pOverlay.Figures.Add(mark);
                }
            }

            if (m_valleysChecked)
            {
                for (int i = 0; i < lsValley.Count; i++)
                {
                    double freq = result[lsValley[i]];
                    double ratio = freq / result.MaxFrequency;
                    FVIL.Data.CFviPoint valleyPoint = new FVIL.Data.CFviPoint(lsValley[i], vert - (vert * ratio));
                    FVIL.GDI.CFviGdiPoint gValleyPoint = new FVIL.GDI.CFviGdiPoint(valleyPoint);
                    gValleyPoint.Pen.Color = System.Drawing.Color.Orange;
                    gValleyPoint.Pen.Width = 2;
                    gValleyPoint.Style = FVIL.GDI.FigureStyle.Xmark;
                    pOverlay.Figures.Add(gValleyPoint);

                    FVIL.GDI.CFviGdiString mark = new FVIL.GDI.CFviGdiString();
                    mark.Text = lsValley[i].ToString();
                    mark.Font.Width = 6;
                    mark.Font.Height = 12;
                    mark.Align = FVIL.GDI.TextAlign.Left | FVIL.GDI.TextAlign.Bottom;
                    mark.Color = System.Drawing.Color.Orange;
                    mark.Position = valleyPoint;
                    pOverlay.Figures.Add(mark);
                }
            }
        }

        public static FVIL.Data.CFviImage drawHistogram(FVIL.Data.CFviImage src)
        {
            FVIL.Data.CFviImage dst = new FVIL.Data.CFviImage();
            const uint _CHANNEL_MAX = 3;
            FVIL.Measure.CFviHistogram parser = new FVIL.Measure.CFviHistogram();
            FVIL.Measure.CFviHistogramResult[] result =
                {
                     new FVIL.Measure.CFviHistogramResult(),
                     new FVIL.Measure.CFviHistogramResult(),
                     new FVIL.Measure.CFviHistogramResult(),
                };
            if (!(src.Channel <= _CHANNEL_MAX))
            {
                return null;
            }
            if (FVIL.ErrorCode._SUCCESS != FVIL.Measure.CFviHistogram.CheckValidity(src))
                return null;
            parser.SrcImages[0] = src;

            for (int ch = 0; ch < src.Channel; ch++)
            {
                switch (src.ImageType)
                {
                    default:
                        return null;
                    case FVIL.ImageType.UC8:
                        result[ch].Resize(256);
                        break;
                    case FVIL.ImageType.S16:
                    case FVIL.ImageType.US16:
                        result[ch].Resize(65535);
                        break;
                }
                parser.Result = result[ch];
                parser.Channel = (uint)ch;
                parser.Execute();
            }

            switch (src.ImageType)
            {
                case FVIL.ImageType.UC8:
                    break;
                default:
                case FVIL.ImageType.S16:
                case FVIL.ImageType.US16:
                    return null;
            }

            {
                System.Drawing.Color[] color =
                {
                    System.Drawing.Color.FromArgb(0xFF, 0x00, 0x00),    
                    System.Drawing.Color.FromArgb(0x00, 0xFF, 0x00),   
                    System.Drawing.Color.FromArgb(0x00, 0x00, 0xFF)       
                };

                int horz = 256;
                int vert = 192;
                int offsetBorder = 22;
                FVIL.Data.CFviImage bg = new FVIL.Data.CFviImage(horz + offsetBorder * 2,
                    vert + offsetBorder * 2, FVIL.ImageType.UC8, 1);
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0,
                    horz + offsetBorder * 2, vert + offsetBorder * 2);
                FVIL.GDI.CFviDisplay display = new FVIL.GDI.CFviDisplay();
                display.Image = bg;
                display.DisplayRect = rect;

                FVIL.GDI.CFviOverlay pOverlay0 = new FVIL.GDI.CFviOverlay();
                pOverlay0.Scaling = false;
                display.Overlays.Add(pOverlay0);
                if (m_axesChecked)
                {
                    drawRuler(ref pOverlay0, new FVIL.Data.CFviPoint(0, vert + offsetBorder),
                        new FVIL.Data.CFviPoint(horz, vert + offsetBorder), false);
                    drawRuler(ref pOverlay0, new FVIL.Data.CFviPoint(horz + offsetBorder, vert),
                        new FVIL.Data.CFviPoint(horz + offsetBorder, 0), true);
                }
                List<int>[] lsPeak = new List<int>[src.Channel];
                List<int>[] lsValley = new List<int>[src.Channel];

                findPeaks(result, ref lsPeak, ref lsValley);
                for (int ch = 0; ch < src.Channel; ch++)
                {
                    if (hasCheckedColor((long)Math.Pow(2, ch + 1)))
                    {
                        FVIL.GDI.CFviGdiPolyline poly = new FVIL.GDI.CFviGdiPolyline();
                        poly.Resize(horz);
                        poly.Close = false;

                        uint freq_min = result[ch].MinFrequency;
                        uint freq_max = result[ch].MaxFrequency;

                        for (int i = 0; i < result[ch].Count; i++)
                        {
                            double freq = result[ch][i];
                            double ratio = freq / freq_max;
                            poly[i] = new FVIL.Data.CFviPoint(i, vert - (vert * ratio));
                        }
                        poly.Pen.Color = color[ch];
                        pOverlay0.Figures.Add(poly);

                        drawPeaks(ref pOverlay0, result[ch], lsPeak[ch], lsValley[ch], vert);
                    }
                }
                display.SaveImage(dst, display.DisplayRect, 1.0);
            }
            return dst;
        }
        #endregion

        #region Histogram Transformation
        public static FVIL.Data.CFviImage equalizeHistogram(FVIL.Data.CFviImage src)
        {
            FVIL.Enhance.CFviHistogramEqualization parser = new FVIL.Enhance.CFviHistogramEqualization();
            FVIL.Data.CFviImage dst = new FVIL.Data.CFviImage();
            int valid = FVIL.Enhance.CFviHistogramEqualization.CheckValidity(src);
            if (valid != FVIL.ErrorCode._SUCCESS)
                throw new FVIL.CFviException(valid, "FVIL.Enhance.CFviHistogramEqualization.CheckValidity");
            parser.SrcImages[0] = src;
            parser.DstImages[0] = dst;
            if (!parser.IsValid())
                parser.Validate();

            parser.Execute();
            return dst;
        }

        public static FVIL.Data.CFviImage normalizeHistogram(FVIL.Data.CFviImage src)
        {
            FVIL.Enhance.CFviHistogramNormalization parser = new FVIL.Enhance.CFviHistogramNormalization();
            FVIL.Data.CFviImage dst = new FVIL.Data.CFviImage();
            int valid = FVIL.Enhance.CFviHistogramNormalization.CheckValidity(src);
            if (valid != FVIL.ErrorCode._SUCCESS)
                throw new FVIL.CFviException(valid, "FVIL.Enhance.CFviHistogramNormalization.CheckValidity");
            parser.SrcImages[0] = src;
            parser.DstImages[0] = dst;
            if (!parser.IsValid())
                parser.Validate();
            parser.Max = 255;
            parser.Min = 0;
            parser.Threshold = 200;
            parser.Execute();
            return dst;
        }

        #endregion

    }
}
